package cn.ivicar.sim.controller;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.inject.Inject;

import cn.com.carsmart.exception.BadRequestException;
import cn.com.carsmart.exception.CarsmartException;
import cn.com.carsmart.ws.model.AppPay;
import cn.com.carsmart.ws.model.FlowTask;
import cn.com.carsmart.ws.model.PayRecord;
import cn.com.carsmart.ws.model.SimChargeRecord;
import cn.com.carsmart.ws.model.SimFlowDailyStat;
import cn.com.carsmart.ws.model.SimFlowInfo;
import cn.com.carsmart.ws.model.SimFlowMonthlyStat;
import cn.com.carsmart.ws.model.SimFlowPackageInfo;
import cn.com.carsmart.ws.model.SimInfo;
import cn.ivicar.sim.domain.FlowStatQuery;
import cn.ivicar.sim.domain.SimInfoQuery;
import cn.ivicar.sim.service.AppPayService;
import cn.ivicar.sim.service.FlowTaskService;
import cn.ivicar.sim.service.SimRpcService;
import cn.ivicar.sim.service.SimService;
import cn.ivicar.sim.util.ConvertUtils;
import cn.ivicar.sim.util.DateUtil;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;

/**
 * Created by lixiaozhuo on 17/5/14.
 */
public class SimController {

	private static transient final Logger LOGGER = LoggerFactory.getLogger(SimController.class);

	@Inject
	private SimService simService;

	// 未支付
	private static final Integer PAY_STATUS_NON = 1;
	// 支付成功
	private static final Integer PAY_STATUS_SUCCESS = 2;
	// 支付失败
	private static final Integer PAY_STATUS_FAIL = 3;
	// 充值成功
	private static final Integer PAY_STATUS_CHARGE_SUCCESS = 4;
	// 充值失败
	private static final Integer PAY_STATUS_CHARGE_FAIL = 5;
	// 支付类型-微信支付
	private static final Integer PAY_TYPE_WECHAT = 1;

	private static Cache<String, JSONObject> simInfoCache = CacheBuilder.newBuilder().maximumSize(100)
			.expireAfterWrite(15, TimeUnit.MINUTES).build();
	@Inject
	private AppPayService appPayService;
	@Inject
	private FlowTaskService flowTaskService;

	/**
	 * 查看卡信息
	 * 
	 * @param iccid
	 *            卡号
	 * @return 卡信息
	 */
	public SimInfo getSimInfo(String iccid) throws BadRequestException {
		if (StringUtils.isBlank(iccid)) {
			throw new BadRequestException("400", "请求参数不能为空!");
		}

		SimInfo simInfo = simService.getSimInfoByIccid(iccid);
		if (simInfo != null) {
			return simInfo;
		}

		JSONObject json = SimRpcService.getSimInfo(iccid);
		JSONObject jsonSimStatus = SimRpcService.getSimStatusRealtime(iccid);
		JSONObject jsonGPRSStatus = SimRpcService.getGPRSStatusRealtime(iccid);
		JSONObject jsonSwitchStatus = SimRpcService.getSwitchStatusRealtime(iccid);
		simInfo = ConvertUtils.convertSimInfo(json, jsonSimStatus, jsonGPRSStatus, jsonSwitchStatus);
		if (simInfo != null) {
			Date now = new Date();
			simInfo.setCreated(now);
			simInfo.setModified(now);
			simService.insertSimInfo(simInfo);
		}

		return simInfo;
	}

	public static void main(String[] args) {
		String iccid = "898607B1101730308339";
		JSONObject json = SimRpcService.getSimInfo(iccid);
		System.out.println("json=" + json);
		JSONObject jsonSimStatus = SimRpcService.getSimStatusRealtime(iccid);
		System.out.println("jsonSimStatus=" + jsonSimStatus);
		JSONObject jsonGPRSStatus = SimRpcService.getGPRSStatusRealtime(iccid);
		System.out.println("jsonGPRSStatus=" + jsonGPRSStatus);
		JSONObject jsonSwitchStatus = SimRpcService.getSwitchStatusRealtime(iccid);
		System.out.println("jsonSwitchStatus=" + jsonSwitchStatus);
		SimInfo simInfo = ConvertUtils.convertSimInfo(json, jsonSimStatus, jsonGPRSStatus, jsonSwitchStatus);
		System.out.println(simInfo);
	}

	public void updateSwitchStatus(String iccid, String targetStatus) throws BadRequestException {
		if (StringUtils.isBlank(iccid) || StringUtils.isBlank(targetStatus)) {
			throw new BadRequestException("400", "请求参数不能为空!");
		}

		JSONObject rpcResult = SimRpcService.updateSwitchStatus(iccid, targetStatus);
		if (rpcResult == null || !ConvertUtils.SUCCESS_CODE.equals(rpcResult.opt(ConvertUtils.KEY_RETURN_CODE))) {
			LOGGER.error("停复机操作失败：rpcResult=" + rpcResult.toString());
			throw new BadRequestException("400", "修改失败!");
		}else{
			LOGGER.info("停复机操作成功：rpcResult=" + rpcResult.toString());
		}

		SimInfo simInfo = new SimInfo();
		simInfo.setIccid(iccid);
		simInfo.setSwitchStatus(targetStatus);
		simInfo.setModified(new Date());
		simService.updateSelectiveSimInfo(simInfo);
	}

	public void updateGPRSStatus(String iccid, String targetStatus) throws BadRequestException {
		if (StringUtils.isBlank(iccid) || StringUtils.isBlank(targetStatus)) {
			throw new BadRequestException("400", "请求参数不能为空!");
		}

		JSONObject rpcResult = SimRpcService.updateGPRSStatus(iccid, targetStatus);
		if (rpcResult == null || !ConvertUtils.SUCCESS_CODE.equals(rpcResult.opt(ConvertUtils.KEY_RETURN_CODE))) {
			LOGGER.error("进行GPRS功能暂停/恢复操作失败：rpcResult=" + rpcResult.toString());
			throw new BadRequestException("400", "修改失败!");
		}else{
			LOGGER.info("进行GPRS功能暂停/恢复操作成功：rpcResult=" + rpcResult.toString());
		}

		SimInfo simInfo = new SimInfo();
		simInfo.setIccid(iccid);
		simInfo.setGprsStatus(targetStatus);
		simInfo.setModified(new Date());
		simService.updateSelectiveSimInfo(simInfo);
	}

	/**
	 * 根据条件查询卡信息列表
	 * 
	 * @param query
	 *            查询条件
	 * @return 卡信息列表
	 */
	public List<SimInfo> getSimInfoList(SimInfoQuery query) {
		return simService.getSimInfoList(query);
	}

	/**
	 * 根据条件查询卡信息数量
	 * 
	 * @param query
	 *            查询条件
	 * @return 卡信息数量
	 */
	public int getSimInfoCount(SimInfoQuery query) {
		return simService.getSimInfoCount(query);
	}

	/**
	 * 查看卡信息
	 * 
	 * @param iccid
	 *            卡号
	 * @return 流量信息
	 */
	public SimFlowInfo getFlowInfo(String iccid) throws BadRequestException {
		if (StringUtils.isBlank(iccid)) {
			throw new BadRequestException("400", "请求参数不能为空!");
		}
		// TODO 添加缓存，避免rpc请求性能问题

		JSONObject json = SimRpcService.getSimInfo(iccid);
		SimFlowInfo latestSimFlowInfo = ConvertUtils.convertSimFlowInfo(json);
		if (latestSimFlowInfo == null) {
			return null;
		}

		SimFlowInfo exist = simService.getSimFlowInfoByIccid(iccid);
		if (exist == null) {
			simService.insertSimFlowInfo(latestSimFlowInfo);
		} else {
			latestSimFlowInfo.setId(exist.getId());
			latestSimFlowInfo.setCreated(exist.getCreated());
			simService.updateSimFlowInfo(latestSimFlowInfo);
		}

		return latestSimFlowInfo;
	}

	/**
	 * 根据条件查询流量信息列表
	 * 
	 * @param query
	 *            查询条件
	 * @return 流量信息列表
	 */
	public List<SimFlowInfo> getFlowInfoList(SimInfoQuery query) {
		return simService.getFlowInfoList(query);
	}

	/**
	 * 根据条件查询流量信息数量
	 * 
	 * @param query
	 *            查询条件
	 * @return 流量信息数量
	 */
	public int getFlowInfoCount(SimInfoQuery query) {
		return simService.getFlowInfoCount(query);
	}

	/**
	 * 根据条件查询流量日统计信息列表
	 * 
	 * @param query
	 *            查询条件
	 * @return 流量日统计信息列表
	 */
	public List<SimFlowDailyStat> getFlowDailyStatList(FlowStatQuery query) {
		return simService.getFlowDailyStatList(query);
	}

	/**
	 * 根据条件查询流量日统计信息数量
	 * 
	 * @param query
	 *            查询条件
	 * @return 流量日统计信息数量
	 */
	public int getFlowDailyStatCount(FlowStatQuery query) {
		return simService.getFlowDailyStatCount(query);
	}

	/**
	 * 根据条件查询流量月统计信息列表
	 * 
	 * @param query
	 *            查询条件
	 * @return 流量月统计信息列表
	 */
	public List<SimFlowMonthlyStat> getFlowMonthlyStatList(FlowStatQuery query) {
		return simService.getFlowMonthlyStatList(query);
	}

	/**
	 * 根据条件查询流量月统计信息数量
	 * 
	 * @param query
	 *            查询条件
	 * @return 流量月统计信息数量
	 */
	public int getFlowMonthlyStatCount(FlowStatQuery query) {
		return simService.getFlowMonthlyStatCount(query);
	}

	/**
	 * 查询指定卡的历史套餐情况
	 * 
	 * @param iccid
	 *            卡号
	 * @return 历史套餐情况
	 */
	public List<SimFlowPackageInfo> getFlowPackageHisList(String iccid) {
		return null;
	}

	/**
	 * 查询指定卡的可使用套餐列表
	 * 
	 * @param iccid
	 *            卡号
	 * @return 可使用套餐列表
	 */
	public List<SimFlowPackageInfo> getAvailableFlowPackageList(String iccid) {
		// TODO 查询结果是否需要缓存 jvm一天缓存？

		JSONArray array = SimRpcService.getAvailableFlowPackage(iccid);
		if (array == null || array.size() < 1) {
			return null;
		}

		List<SimFlowPackageInfo> list = new ArrayList<SimFlowPackageInfo>();
		for (int i = 0; i < array.size(); i++) {
			SimFlowPackageInfo simFlowPackageInfo = ConvertUtils.convertFLowPackage(array.getJSONObject(i));
			list.add(simFlowPackageInfo);
			// TODO 保存和更新记录到流量包信息表中
			simFlowPackageInfo.setIsDeleted(true);
			simService.save(simFlowPackageInfo);
		}

		// TODO 保存和更新记录到流量包信息表中

		return list;
	}

	/**
	 * 购买套餐接口
	 * <dependency>
	 * 			<groupId>cn.com.carsmart</groupId>
	 * 			<artifactId>db-common</artifactId>
	 * 		</dependency>
	 * @param iccid
	 *            卡号
	 * @param flowPackageId
	 *            套餐编号
	 * @param appId
	 * @return 若购买成功，则返回对应的订单ID和支付ID；失败则抛异常
	 */
	public JSONObject buyFlowPackage(Long customerId,String iccid, String flowPackageId, String appId, String ip)
			throws CarsmartException {

		SimFlowPackageInfo packageInfo = simService.getSimFlowPackageInfoById(flowPackageId);
		if (packageInfo == null) {
			return null;
		}

		// TODO:此处默认微信APP支付
		AppPay appPayInfo = appPayService.getByAppIdAndType(appId, 1);
		Date now = new Date();
		SimChargeRecord simChargeRecord = new SimChargeRecord();
		simChargeRecord.setCustomerId(customerId);
		simChargeRecord.setIccid(iccid);
		simChargeRecord.setFlowPackageId(packageInfo.getFlowPackageId());
		// TODO 金额
		simChargeRecord.setChargeAmount(packageInfo.getPrice());
		simChargeRecord.setChargeTime(now);
		simChargeRecord.setChargeId(generateChargeId(now));
		simChargeRecord.setPayAppId(appPayInfo.getPayAppId());

		JSONObject prePay = SimRpcService.payByWechat(simChargeRecord, ip, appPayInfo,packageInfo.getName());

		// 保存支付记录
		PayRecord payRecord = new PayRecord();
		payRecord.setPayType(PAY_TYPE_WECHAT);
		payRecord.setPrePayId(prePay.optString("prepay_id", ""));
		payRecord.setPayStatus(PAY_STATUS_NON);
		// TODO 金额
		payRecord.setPayAmount(packageInfo.getPrice());
		payRecord.setPayTime(now);
		payRecord.setCreated(now);
		payRecord.setModified(now);
		simService.insertPayRescord(payRecord);

		// 保存订单记录
		simChargeRecord.setChargeStatus(PAY_STATUS_NON);
		simChargeRecord.setCreated(now);
		simChargeRecord.setModified(now);
		simChargeRecord.setPayRecordId(payRecord.getId());
		simChargeRecord.setIsDeleted(false);
		simService.insertSimChargeRescord(simChargeRecord);

		prePay.put("payId", payRecord.getId());
		prePay.put("chargeId", simChargeRecord.getChargeId());

		return prePay;
	}

	private Long generateChargeId(Date now) {
		String str = DateUtil.TIMEFORMATER4().format(now) + ((int) (Math.random() * 900) + 100);
		return Long.parseLong(str);
	}

	/**
	 * 充值回调接口
	 *
	 * @param params
	 *            微信回调参数
	 */
	public void callBack(Map<String, String> params) throws Exception {
		if (params == null) {
			throw new BadRequestException("400", "请求参数不能为空!");
		}

		boolean payStatus = params.get(SimRpcService.KEY_RETURN_CODE).equals(SimRpcService.SUCCESS_FLAG);
		Long chargeId = Long.parseLong(params.get("out_trade_no"));
		SimChargeRecord simChargeRecord = simService.getChargeRecordById(chargeId);
		if (simChargeRecord == null) {
			LOGGER.warn(String.format("系统中不存在订单[chargeId=%s]，不在处理", chargeId));
			return;
		}

		if (!PAY_STATUS_NON.equals(simChargeRecord.getChargeStatus())) {
			LOGGER.warn(String.format("订单[chargeId=%s]不是未支付状态，不在处理", chargeId));
			return;
		}

		PayRecord payRecord = simService.getPayRecordById(simChargeRecord.getPayRecordId());
		if (payRecord == null) {
			LOGGER.warn(String.format("订单[chargeId=%s]在系统中没有对应的支付记录，不在处理", chargeId));
			return;
		}

		if (payStatus) {
			// 支付成功

			String payAppid = params.get("appid");
			// TODO:此处默认微信APP支付
			AppPay appPayInfo = appPayService.getByPayAppIdAndType(payAppid, 1);

			// TODO 签名验证
			if (!params.get("sign").equals(
					SimRpcService.createAccessToken(new TreeMap<String, String>(params), appPayInfo.getAppKey()))) {
				LOGGER.error(String.format("订单[chargeId=%s]回调信息签名[sign=%s]不合法，不在处理", chargeId, params.get("sign")));
				return;
			}

			// TODO 金额一致性验证
			if (!params.get("cash_fee")
					.equals(simChargeRecord.getChargeAmount().multiply(BigDecimal.valueOf(100)).intValue() + "")) {
				LOGGER.error(
						String.format("订单[chargeId=%s]回调支付金额[cash_fee=%s]不一致，不在处理", chargeId, params.get("cash_fee")));
				return;
			}

			// 更新支付记录
			payRecord.setPayStatus(PAY_STATUS_SUCCESS);
			simService.updatePayRescord(payRecord);

			SimFlowPackageInfo packageInfo = simService.getSimFlowPackageInfoById(simChargeRecord.getFlowPackageId());
			if (packageInfo == null) {
				return;
			}

			simChargeRecord.setChargeStatus(PAY_STATUS_CHARGE_SUCCESS);
			simService.updateSimChargeRecord(simChargeRecord);

//			// 调rpc接口进行充值
//			JSONObject json = SimRpcService.buyFlowPackage(simChargeRecord.getIccid(), packageInfo.getRefPackageId(),
//					simChargeRecord.getChargeId().toString());
//			// 更新充值记录
//			if (json != null) {
//				simChargeRecord.setChargeStatus(PAY_STATUS_CHARGE_SUCCESS);
//				simService.updateSimChargeRecord(simChargeRecord);
//				int month = packageInfo.getActiveMonth();
//				for (int i = 1; i < month; i++) {
//					FlowTask task = new FlowTask();
//					task.setIccid(simChargeRecord.getIccid());
//					task.setFlowPackageId(packageInfo.getRefPackageId());
//					task.setExecuteStatus(1);
//					Calendar c = Calendar.getInstance();
//					c.set(Calendar.DAY_OF_MONTH, 1);
//					c.add(Calendar.MONTH, month);
//					task.setExecuteTime(c.getTime());
//					flowTaskService.insert(task);
//				}
//
//			} else {
//				simChargeRecord.setChargeStatus(PAY_STATUS_CHARGE_FAIL);
//				simService.updateSimChargeRecord(simChargeRecord);
//				LOGGER.error(
//						String.format("购买流量包失败[iccid:%s,floowPackageId:%s,chargeId:%s]!", simChargeRecord.getIccid(),
//								packageInfo.getRefPackageId(), simChargeRecord.getChargeId().toString()));
//			}
		} else {
			// 更新支付记录
			payRecord.setPayStatus(PAY_STATUS_FAIL);
			simService.updatePayRescord(payRecord);

			// 更新充值记录
			simChargeRecord.setChargeStatus(PAY_STATUS_FAIL);
			simService.updateSimChargeRecord(simChargeRecord);
		}
	}

	/**
	 * 实时调用第三方api查询流量信心
	 */
	public JSONObject getSimFlowInfo(final String iccid) {
		// TODO 查询结果缓存15min
		if (StringUtils.isBlank(iccid)) {
			return null;
		}
		JSONObject result = null;
		try {
			result = simInfoCache.get(iccid, new Callable<JSONObject>() {

				public JSONObject call() throws Exception {
					JSONObject object = SimRpcService.getSimInfo(iccid);
					if (object == null) {
						return null;
					}

					JSONObject result = new JSONObject();
					result.put("iccid", object.optString("iccid", ""));
					result.put("userCode", object.optString("code", ""));
					result.put("totalFlow", object.optString("trafficTotal", ""));
					result.put("unuse", object.optString("validFlow", ""));
					result.put("operator", object.optString("operator", ""));
					result.put("cardStatus", object.optString("carStatus", ""));
					return result;
				}

			});
		} catch (Exception e) {
			LOGGER.error(String.format("获取SIM卡信息[iccid=%s]失败", iccid), e);
		}

		return result;
	}

	public JSONObject getAvailableFlowPackageArray(String iccid) throws CarsmartException {
		// TODO 查询结果是否需要缓存 jvm一天缓存？

		if (StringUtils.isBlank(iccid)) {
			return null;
		}

		SimInfo simInfo = this.getSimInfo(iccid);
		
		if(simInfo == null){
			throw new BadRequestException("400", "查询不到此流量卡");
		}

		List<SimFlowPackageInfo> array = simService.getSimFlowPackageInfoByOperator(simInfo.getOperator());
		if (array == null || array.size() < 1) {
			array = this.getAvailableFlowPackageList(iccid);
		}

		if (array == null || array.size() < 1) {
			return null;
		}

		List<JSONObject> list = new ArrayList<JSONObject>();
		for (int i = 0; i < array.size(); i++) {
			SimFlowPackageInfo item = array.get(i);
			JSONObject object = new JSONObject();
			object.put("packageId", item.getFlowPackageId());
			object.put("title", item.getName());
			object.put("flowTotal", item.getFlowTotal());
			object.put("flowMonth", item.getActiveMonth());
			object.put("priority", item.getType());
			object.put("price", item.getPrice());
			list.add(object);
		}

		JSONObject result = new JSONObject();
		result.put("count", list.size());
		result.put("items", list);

		return result;
	}
}
